/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.araujogrosz.fractalcore;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author renatogrosz@gmail.com
 */
public class ThreadFractal implements Zoonable, Calcable{

    private List<CalcFract> calcList;
    int integerSquareThreads;
    double doubleSquareTheads;
    int miniImageSize;
    double miniVetorSize;
    BufferedImage buf;
    

    ThreadFractal(int threads, int imageSize, ColorMatrix colorMatrix) {
        doubleSquareTheads = Math.sqrt(threads);
        integerSquareThreads = (int) doubleSquareTheads;
        if (integerSquareThreads != doubleSquareTheads) {
            throw new RuntimeException("Number of threads must be power of any integer");
        }

        calcList = new ArrayList<>();

        miniImageSize = imageSize / integerSquareThreads;
        miniVetorSize = 4d / (double)integerSquareThreads;

        buf = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_RGB);

        for (int y = 0; y < integerSquareThreads; y++) {
            double startY =  -2 + y * miniVetorSize;
            int py = y * miniImageSize;
            for (int x = 0; x < integerSquareThreads; x++) {
                double startX =  -2 + x * miniVetorSize;
                int px = x * miniImageSize;
                CalcFract c = new CalcFract(startX, startY, miniImageSize, miniVetorSize);
                c.setIterations(4000);
                ImagePainter painter = new ImagePainter(buf, colorMatrix);
                painter.setStart(px, py);
                c.setPrinter(painter);
                this.calcList.add(c);
            }
        }
    }

    @Override
    public void zoom(int x, int y, double fator) {
        int id = 0;
        for (int py = 0; py < integerSquareThreads; py++) {
            int startY  = py * miniImageSize;            
            int clickY = y - startY;
            for (int px = 0; px < integerSquareThreads; px++) {
                CalcFract c = this.calcList.get(id);
                int startX = px * miniImageSize;
                int clickX = x - startX;
                c.zoom(clickX, clickY, fator);
                id+=1;
            }
        }
    }
    
    
    @Override
    public void calc() {
        int id = 0;
        List<Thread> threads = new ArrayList<>();        
        for (int py = 0; py < integerSquareThreads; py++) {
            for (int px = 0; px < integerSquareThreads; px++) {
                CalcFract c = this.calcList.get(id);                
                threads.add(this.createThread(c));                
                id+=1;
            }
        }
        id=0;
        for (int py = 0; py < integerSquareThreads; py++) {
            for (int px = 0; px < integerSquareThreads; px++) {
                Thread t = threads.get(id);
                t.start();
                id+=1;
            }
        }
        
        try{
            id=0;
            for (int py = 0; py < integerSquareThreads; py++) {
                for (int px = 0; px < integerSquareThreads; px++) {
                    Thread t = threads.get(id);
                    t.join();  
                    id +=1;
                }
            }
        }catch(Exception e){
            throw new RuntimeException("Thread join error");
        }
    }            
    
    private Thread createThread(CalcFract c){                
        return new Thread(new RunCalc(c));
    }
    
    private class RunCalc implements Runnable{
        private CalcFract c;
        public RunCalc(CalcFract c){
            this.c = c;
        }
        @Override
        public void run() {
            c.calc();
        }        
    }
    
    public BufferedImage getBuf(){
        return buf;
    }    
}
